import { _decorator, Component, Node, instantiate, Label, v3, EventTouch, SystemEventType, Sprite, Vec2, UITransform, Vec3, Prefab, v2, tween, Tween, EditBox, SpriteFrame, JsonAsset } from "cc";
import { DEBUG } from "cc/env";
import { Dijkstra } from "./Dijkstra";
import { eventCenter, EVENT_NAME } from "./EventCenter";
import { mapData, MAP_CELL_NUM, MAP_CELL_SIZE } from "./MapData";
import { ResourceManager } from "./ResourceManager";
import { Toast } from "./Toast";
import { saveData, saveForBrowser, twoPointDegree } from "./uitils";
const { ccclass, property } = _decorator;

@ccclass("WorldMap")
export class WorldMap extends Component {
    @property({ type: Node, displayName: "背景容器" })
    mapBgContent!: Node;

    @property({ type: Node, displayName: "城池容器" })
    mapCityContent!: Node;

    @property({ type: Prefab, displayName: "城池预制" })
    cityPrefab!: Prefab;

    @property({ type: Prefab, displayName: "城池之间的连线" })
    cityLinePrefab!: Prefab;

    @property({ type: EditBox, displayName: "X输入框" })
    xInputTxt!: EditBox;

    @property({ type: EditBox, displayName: "Y输入框" })
    yIputTxt!: EditBox;

    // [2]
    // @property
    // serializableDummy = 0;

    selectedMapBlockCell: Node | undefined;
    selectedCityCell: Node | undefined;

    private currentSelectedCity: Node | undefined; // 当前选中城池
    private currentSelectedLine: Node | undefined; // 待移除的连线

    private mathNumberFloorInt = Math.floor;
    private terrianIdStartNum = 0;
    onLoad() {
        this.mapCityContent.on(SystemEventType.TOUCH_START, this.touchMapStart, this);
        this.mapCityContent.on(SystemEventType.TOUCH_MOVE, this.touchMapMove, this);
        this.mapCityContent.on(SystemEventType.TOUCH_END, this.touchMapEnd, this);
        this.mapCityContent.on(SystemEventType.TOUCH_CANCEL, this.touchMapCancel, this);

        eventCenter.on(
            EVENT_NAME.SELECTED_TEMP_CELL,
            (selectedNode: Node) => {
                if (selectedNode.name.indexOf("city") !== -1) {
                    this.selectedCityCell = selectedNode;
                    this.mapCityContent.active = true;
                } else {
                    this.selectedMapBlockCell = selectedNode;
                    this.mapCityContent.active = false;
                }
            },
            this
        );

        eventCenter.on(
            EVENT_NAME.CANCEL_TEMP_CELL,
            () => {
                this.selectedMapBlockCell = undefined;
                this.selectedCityCell = undefined;
            },
            this
        );

        eventCenter.on(EVENT_NAME.LINE_CITY_TO_CITY, (fromCityNode: Node, toCityNode: Node) => {
            const lineName = fromCityNode.name + "-" + toCityNode.name;
            // 当前不存在连线
            if (!mapData.terrianLines[toCityNode.name + "-" + fromCityNode.name] && !mapData.terrianLines[lineName]) {
                if (this.cityLinePrefab) {
                    const fromPos = fromCityNode.position;
                    const toPos = toCityNode.position;
                    this.addLine(lineName, v2(fromPos.x, fromPos.y), v2(toPos.x, toPos.y));
                }
            } else {
                Toast.showToast(this.node, "已存在连线");
            }
        });

        mapData.loadMapJson(() => {
            this.initMapView();
        });
    }

    start() {
        // [3]
    }

    onDestroy() {
        if (this.mapCityContent && this.mapBgContent.isValid) {
            this.mapCityContent.targetOff(this);
        }
        eventCenter.targetOff(this);
    }

    private touchMapStart(event: EventTouch) {
        const touchPos = event.getUILocation();
    }

    private touchMapMove(event: EventTouch) {
        const touchPos = event.getUILocation();
    }

    private touchMapEnd(event: EventTouch) {
        const touchPos = event.getUILocation();
        const startTouchPos = event.getUIStartLocation();
        console.log(touchPos);

        const disLen = Vec2.distance(startTouchPos, touchPos);
        if (disLen < 6) {
            // 认为是点击事件
            if (this.selectedCityCell) {
                const pos = new Vec3();
                const cityTrans = this.mapCityContent.getComponent(UITransform);
                cityTrans?.convertToNodeSpaceAR(v3(touchPos.x, touchPos.y, 0), pos);
                const terrianType = this.selectedCityCell.getChildByName("name")!.getComponent(Label)!.string;
                const cityName = `${this.terrianIdStartNum++}`;
                this.addCity(cityName, Number(terrianType), v2(this.mathNumberFloorInt(pos.x), this.mathNumberFloorInt(pos.y)));
            }
        }
    }

    private touchMapCancel(event: EventTouch) {}

    initMapView() {
        // 背景
        for (let i = 0; i < MAP_CELL_NUM.tileRow; i++) {
            for (let j = 0; j < MAP_CELL_NUM.tileColno; j++) {
                const bgIdx = i * MAP_CELL_NUM.tileColno + j;
                const blockName = i + "-" + j;
                let bgBlockNode = this.mapBgContent.children[bgIdx];
                if (!bgBlockNode) {
                    bgBlockNode = instantiate(this.mapBgContent.children[0]);
                    bgBlockNode.parent = this.mapBgContent;
                }
                bgBlockNode.position = v3(j * MAP_CELL_SIZE.tileWidth, i * MAP_CELL_SIZE.tileHeight, 0);
                const blockId = i + "-" + j;
                // console.log(i + "-" + j, j * MAP_CELL_SIZE.tileWidth, i * MAP_CELL_SIZE.tileHeight);
                if (DEBUG) {
                    bgBlockNode.getChildByName("block_num")!.getComponent(Label)!.string = blockName;
                }
                const resName = mapData.bgBlockList[blockName] ? mapData.bgBlockList[blockName] : "block" + (MAP_CELL_NUM.tileRow - i) + "_" + (j + 1);
                ResourceManager.getInstance()
                    .loadPath<SpriteFrame>("textures/map/" + resName + "/spriteFrame")
                    .then((sprFame: SpriteFrame) => {
                        bgBlockNode.getComponent(Sprite)!.spriteFrame = sprFame;
                        mapData.bgBlockList[blockName] = sprFame!.name;
                    });
                bgBlockNode.on(Node.EventType.TOUCH_START, () => {
                    if (this.selectedMapBlockCell) {
                        const selectedSpriteFrame = this.selectedMapBlockCell.getComponent(Sprite)!.spriteFrame;
                        bgBlockNode.getComponent(Sprite)!.spriteFrame = selectedSpriteFrame;
                        mapData.bgBlockList[blockName] = selectedSpriteFrame!.name;
                    }
                });
            }
        }

        // 城池
        for (const cityId in mapData.terrianList) {
            const cityData = mapData.terrianList[cityId];
            const [cityX, cityY] = cityData.pos.split("-");
            this.addCity(cityId, cityData.type, v2(Number(cityX), Number(cityY)));
        }
        // 连线
        for (const lineId in mapData.terrianLines) {
            const lineData = mapData.terrianLines[lineId];
            const [lineX, lineY] = lineData.startPos.split("-");
            const [lineX2, lineY2] = lineData.toPos.split("-");
            this.addLine(lineId, v2(Number(lineX), Number(lineY)), v2(Number(lineX2), Number(lineY2)));
        }

        // 当前地形id列表长度
        const totleNum = Object.keys(mapData.terrianList).length;
        this.terrianIdStartNum = totleNum > 0 ? Number(Object.keys(mapData.terrianList)[totleNum - 1]) : 0;
    }

    private addCity(cityName: string, type: number, cityPos: Vec2) {
        const cityNode = instantiate(this.cityPrefab);
        cityNode.name = cityName;
        cityNode.parent = this.mapCityContent;
        cityNode.position = v3(cityPos.x, cityPos.y, 0);

        cityNode.getChildByName("name")!.getComponent(Label)!.string = `${cityName}`;
        ResourceManager.getInstance()
            .loadPath<SpriteFrame>("textures/map/terrian-" + type + "/spriteFrame")
            .then((sprFame: SpriteFrame) => {
                cityNode.getComponent(Sprite)!.spriteFrame = sprFame;
            });
        cityNode.on(Node.EventType.TOUCH_START, () => {
            if (this.currentSelectedCity) {
                if (this.currentSelectedCity !== cityNode) {
                    eventCenter.emit(EVENT_NAME.LINE_CITY_TO_CITY, this.currentSelectedCity, cityNode);
                    this.currentSelectedCity.getChildByName("selected")!.active = false;
                    this.currentSelectedCity = undefined;
                } else {
                    this.currentSelectedCity.getChildByName("selected")!.active = false;
                    this.currentSelectedCity = undefined;
                }
            } else {
                this.currentSelectedCity = cityNode;
                cityNode.getChildByName("selected")!.active = true;
            }
        });

        mapData.terrianList[cityName] = { type: type, pos: cityPos.x + "-" + cityPos.y };
    }

    private addLine(lineName: string, startPos: Vec2, endPos: Vec2) {
        const cityLineNode = instantiate(this.cityLinePrefab);
        cityLineNode.name = lineName;
        cityLineNode.parent = this.mapCityContent;
        const fromPos = v3(startPos.x, startPos.y, 0);
        const toPos = v3(endPos.x, endPos.y, 0);
        const disLen = Math.floor(Vec3.distance(fromPos, toPos));
        cityLineNode.position = fromPos;
        cityLineNode.getComponent(UITransform)!.width = disLen;
        const degree = twoPointDegree(v2(fromPos.x, fromPos.y), v2(toPos.x, toPos.y));
        cityLineNode.angle = degree;

        cityLineNode.on(Node.EventType.TOUCH_START, () => {
            if (this.currentSelectedLine === cityLineNode) {
                Tween.stopAllByTag(1);
                cityLineNode.setScale(1, 1, 1);
                this.currentSelectedLine = undefined;
            } else {
                this.currentSelectedLine = cityLineNode;
                this.showSelectedEffect(cityLineNode);
            }
        });

        mapData.terrianLines[lineName] = { startPos: fromPos.x + "-" + fromPos.y, toPos: toPos.x + "-" + toPos.y, len: disLen };
    }

    private showSelectedEffect(currentNode: Node) {
        tween(currentNode)
            .to(1, { scale: new Vec3(1, 3, 1) })
            .to(1, { scale: new Vec3(1, 1, 1) })
            .union()
            .repeatForever()
            .tag(1)
            .start();
    }

    // 移除当前选中节点
    private removeSelectedNode() {
        if (this.currentSelectedLine) {
            delete mapData.terrianLines[this.currentSelectedLine.name];
            this.currentSelectedLine.removeFromParent();
            this.currentSelectedLine = undefined;
        }
        if (this.currentSelectedCity) {
            for (const cityId in mapData.terrianList) {
                if (cityId === this.currentSelectedCity.name) {
                    delete mapData.terrianList[cityId];
                }
            }
            Object.keys(mapData.terrianLines).forEach((lineName) => {
                if (lineName.startsWith(this.currentSelectedCity!.name + "-") || lineName.endsWith("-" + this.currentSelectedCity!.name)) {
                    this.mapCityContent.getChildByName(lineName)?.removeFromParent();
                    delete mapData.terrianLines[lineName];
                }
            });
            this.currentSelectedCity.removeFromParent();
            this.currentSelectedCity = undefined;
        }
    }

    // 调整偏移量
    private moveSelectedTargetPos(event: Event, customStr: string) {
        const dirNum = Number(customStr);
        const xInputTxt = this.xInputTxt.string;
        const xoffNum = Number(xInputTxt) * dirNum;

        const yInputTxt = this.yIputTxt.string;
        const yoffNum = Number(yInputTxt) * dirNum;

        if (this.currentSelectedCity) {
            const prePos = this.currentSelectedCity?.position.clone();
            this.currentSelectedCity?.setPosition(prePos.add(v3(xoffNum, yoffNum, 0)));
        }
    }

    // 保存数据
    private saveData() {
        const map = {
            bgTile: mapData.bgBlockList,
            terrians: mapData.terrianList,
            lines: mapData.terrianLines,
        };
        const mapStr = JSON.stringify(map);
        saveData(mapStr, "map.json");
    }

    /**
     *  找出最短路径
     * @param startVertex 起始位置
     * @param targetVertex 目标位置
     */
    findPath(startVertex: string, targetVertex: string) {
        startVertex = "0";
        targetVertex = "9";
        const pathVertex: string[] = [targetVertex];
        // 从后向前 遍历顶点 找出路径
        const cityIds = Object.keys(mapData.terrianList);
        const cityMap: Map<string, number> = new Map();
        for (const line in mapData.terrianLines) {
            cityMap.set(line, mapData.terrianLines[line].len);
        }
        const dijks = new Dijkstra(cityIds, cityMap);
        const toVertexPaths = dijks.findPath(startVertex);
        let currentVertex = targetVertex;
        while (toVertexPaths[currentVertex]) {
            currentVertex = toVertexPaths[currentVertex];
            pathVertex.push(currentVertex);
        }
        pathVertex.push(startVertex);
        const paths = pathVertex.reverse();

        console.log(paths);
    }

    // update (deltaTime: number) {
    //     // [4]
    // }
}
